package com.comac.ins.isfd.service.impl;

import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.comac.ins.common.core.exception.ServiceException;
import com.comac.ins.common.core.exception.base.BaseException;
import com.comac.ins.common.core.utils.MapstructUtils;
import com.comac.ins.common.core.utils.StreamUtils;
import com.comac.ins.common.core.utils.StringUtils;
import com.comac.ins.common.core.utils.file.FileUtils;
import com.comac.ins.common.mybatis.core.page.PageQuery;
import com.comac.ins.common.mybatis.core.page.TableDataInfo;
import com.comac.ins.common.oss.core.OssClient;
import com.comac.ins.common.oss.entity.UploadResult;
import com.comac.ins.common.oss.enumd.AccessPolicyType;
import com.comac.ins.common.oss.factory.OssFactory;
import com.comac.ins.isfd.constant.enums.IsfdAcousticsTypeEnum;
import com.comac.ins.isfd.domain.IsfdAcousticsModel;
import com.comac.ins.isfd.domain.bo.IsfdAcousticsModelBo;
import com.comac.ins.isfd.domain.bo.query.IsfdAcousticsModelQueryBo;
import com.comac.ins.isfd.domain.vo.IsfdAcousticsModelVo;
import com.comac.ins.isfd.mapper.IsfdAcousticsModelMapper;
import com.comac.ins.isfd.service.IIsfdAcousticsModelService;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.time.StopWatch;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

/**
 * 声学模型Service业务层处理
 */
@RequiredArgsConstructor
@Service
@Slf4j
public class IsfdAcousticsModelServiceImpl implements IIsfdAcousticsModelService {

    @Autowired
    private IsfdAcousticsModelMapper baseMapper;

    @Value("${evn.evnType}")
    private String evnType;

    // 文件前缀
    private static final String MODEL_OSS_PATH_PREFIX = IsfdAcousticsTypeEnum.ACOUSTICS.getValue() + "/" + IsfdAcousticsTypeEnum.MODEL.getValue() + "/";

    @Override
    public IsfdAcousticsModelVo queryById(Long id) {
        return baseMapper.selectVoById(id);
    }

    @Override
    public TableDataInfo<IsfdAcousticsModelVo> queryPageList(IsfdAcousticsModelQueryBo queryBo) {
        Integer pageNum = queryBo.getPageNum();
        Integer pageSize = queryBo.getPageSize();
        PageQuery pageQuery = new PageQuery();
        pageQuery.setPageNum(pageNum);
        pageQuery.setPageSize(pageSize);
        LambdaQueryWrapper<IsfdAcousticsModel> lqw = buildQueryWrapper(queryBo);
        Page<IsfdAcousticsModelVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
        List<IsfdAcousticsModelVo> filterResult = new ArrayList<>();
        if (evnType.equals("red")) {
            filterResult = StreamUtils.toList(result.getRecords(), this::signUrl);
        } else {
            filterResult = StreamUtils.toList(result.getRecords(), this::matchingUrl);
        }
        result.setRecords(filterResult);
        return TableDataInfo.build(result);
    }

    @Override
    public List<IsfdAcousticsModelVo> queryList(IsfdAcousticsModelBo bo) {
        LambdaQueryWrapper<IsfdAcousticsModel> lqw = buildQueryWrapper(bo);
        return baseMapper.selectVoList(lqw);
    }

    private LambdaQueryWrapper<IsfdAcousticsModel> buildQueryWrapper(IsfdAcousticsModelQueryBo bo) {
        LambdaQueryWrapper<IsfdAcousticsModel> lqw = Wrappers.lambdaQuery();
        lqw.like(StringUtils.isNotBlank(bo.getFileName()), IsfdAcousticsModel::getFileName, bo.getFileName());
        if (bo.getBeginCreateTime() != null && bo.getEndCreateTime() != null) {
            lqw.between(IsfdAcousticsModel::getCreateTime, bo.getBeginCreateTime(), bo.getEndCreateTime());
        }
        lqw.like(StringUtils.isNotBlank(bo.getOriginalName()), IsfdAcousticsModel::getOriginalName, bo.getOriginalName());
        lqw.eq(StringUtils.isNotBlank(bo.getMeshFormat()), IsfdAcousticsModel::getMeshFormat, bo.getMeshFormat());
        lqw.eq(StringUtils.isNotBlank(bo.getUrl()), IsfdAcousticsModel::getUrl, bo.getUrl());
        lqw.eq(StringUtils.isNotBlank(bo.getFilePath()), IsfdAcousticsModel::getFilePath, bo.getFilePath());
        lqw.eq(bo.getFileSize() != null, IsfdAcousticsModel::getFileSize, bo.getFileSize());
        lqw.eq(StringUtils.isNotBlank(bo.getSolver()), IsfdAcousticsModel::getSolver, bo.getSolver());
        lqw.like(StringUtils.isNotBlank(bo.getBusinessName()), IsfdAcousticsModel::getBusinessName, bo.getBusinessName());
        lqw.eq(StringUtils.isNotBlank(bo.getImportMethod()), IsfdAcousticsModel::getImportMethod, bo.getImportMethod());
        lqw.eq(StringUtils.isNotBlank(bo.getRemarks()), IsfdAcousticsModel::getRemarks, bo.getRemarks());
        lqw.orderByDesc(IsfdAcousticsModel::getCreateTime);
        return lqw;
    }

    private LambdaQueryWrapper<IsfdAcousticsModel> buildQueryWrapper(IsfdAcousticsModelBo bo) {
        LambdaQueryWrapper<IsfdAcousticsModel> lqw = Wrappers.lambdaQuery();
        lqw.like(StringUtils.isNotBlank(bo.getFileName()), IsfdAcousticsModel::getFileName, bo.getFileName());
        lqw.like(StringUtils.isNotBlank(bo.getOriginalName()), IsfdAcousticsModel::getOriginalName, bo.getOriginalName());
        lqw.eq(StringUtils.isNotBlank(bo.getMeshFormat()), IsfdAcousticsModel::getMeshFormat, bo.getMeshFormat());
        lqw.eq(StringUtils.isNotBlank(bo.getUrl()), IsfdAcousticsModel::getUrl, bo.getUrl());
        lqw.eq(StringUtils.isNotBlank(bo.getFilePath()), IsfdAcousticsModel::getFilePath, bo.getFilePath());
        lqw.eq(bo.getFileSize() != null, IsfdAcousticsModel::getFileSize, bo.getFileSize());
        lqw.eq(StringUtils.isNotBlank(bo.getSolver()), IsfdAcousticsModel::getSolver, bo.getSolver());
        lqw.like(StringUtils.isNotBlank(bo.getBusinessName()), IsfdAcousticsModel::getBusinessName, bo.getBusinessName());
        lqw.eq(StringUtils.isNotBlank(bo.getImportMethod()), IsfdAcousticsModel::getImportMethod, bo.getImportMethod());
        lqw.eq(StringUtils.isNotBlank(bo.getRemarks()), IsfdAcousticsModel::getRemarks, bo.getRemarks());
        lqw.orderByDesc(IsfdAcousticsModel::getCreateTime);
        return lqw;
    }

    @Override
    public Boolean deleteWithValidByIds(Collection<Long> ids) {
        return baseMapper.deleteBatchIds(ids) > 0;
    }

    @Override
    public Boolean save(MultipartFile file, IsfdAcousticsModelBo bo) {
        String originalFileName = extractFileName(file.getOriginalFilename());

        // 检查模型是否已存在
        if (isModelExists(originalFileName, null)) {
            throw new BaseException("模型文件已存在: " + originalFileName + "，请确认是否覆盖");
        }

        // 上传文件并保存模型信息
        UploadResult uploadResult = uploadFileToOss(file);
        populateModelBoFromUpload(bo, file, originalFileName, uploadResult);

        return insertByBo(bo);
    }

    @Override
    public Boolean saveOrUpdate(MultipartFile file, IsfdAcousticsModelBo bo, boolean overwrite) {
        String originalFileName = extractFileName(file.getOriginalFilename());

        // 检查模型是否已存在
        IsfdAcousticsModel existingModel = findModelByOriginalName(originalFileName);

        if (existingModel != null) {
            if (!overwrite) {
                throw new BaseException("模型文件已存在: " + originalFileName + "，请确认是否覆盖");
            } else {
                // 覆盖已存在的模型
                return updateExistingModel(file, bo, existingModel);
            }
        } else {
            // 新增模型
            return save(file, bo);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean insertByBo(IsfdAcousticsModelBo bo) {
        // 插入前再次检查唯一性
        if (isModelExists(bo.getOriginalName(), null)) {
            throw new BaseException("模型文件已存在: " + bo.getOriginalName());
        }

        IsfdAcousticsModel model = MapstructUtils.convert(bo, IsfdAcousticsModel.class);
        boolean success = baseMapper.insert(model) > 0;

        if (success) {
            bo.setId(model.getId());
        }

        return success;
    }

    @Override
    public Boolean edit(IsfdAcousticsModelBo bo) {
        validateModelUpdate(bo);

        IsfdAcousticsModel update = MapstructUtils.convert(bo, IsfdAcousticsModel.class);
        return baseMapper.updateById(update) > 0;
    }

    @Override
    public void download(Long modelId, HttpServletResponse response) throws IOException {
        IsfdAcousticsModelVo modelVo = baseMapper.selectVoById(modelId);
        if (ObjectUtil.isNull(modelVo)) {
            throw new ServiceException("文件数据不存在!");
        }

        prepareDownloadResponse(response, modelVo.getFileName());

        OssClient storage = OssFactory.instance();
        long contentLength = storage.download(modelVo.getFilePath(), response.getOutputStream());
        response.setContentLengthLong(contentLength);
    }

    // ============ 私有方法 ============

    /**
     * 从文件名中提取纯文件名（不含路径）
     */
    private String extractFileName(String originalFilename) {
        return new File(originalFilename).getName();
    }

    /**
     * 检查模型是否存在
     */
    private boolean isModelExists(String originalName, Long excludeId) {
        if (StringUtils.isBlank(originalName)) {
            return false;
        }

        LambdaQueryWrapper<IsfdAcousticsModel> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.eq(IsfdAcousticsModel::getOriginalName, originalName);

        if (excludeId != null) {
            queryWrapper.ne(IsfdAcousticsModel::getId, excludeId);
        }

        return baseMapper.selectCount(queryWrapper) > 0;
    }

    /**
     * 根据原始文件名查找模型
     */
    private IsfdAcousticsModel findModelByOriginalName(String originalName) {
        if (StringUtils.isBlank(originalName)) {
            return null;
        }

        LambdaQueryWrapper<IsfdAcousticsModel> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.eq(IsfdAcousticsModel::getOriginalName, originalName);

        return baseMapper.selectOne(queryWrapper);
    }

    /**
     * 上传文件到OSS
     */
    private UploadResult uploadFileToOss(MultipartFile file) {
        StopWatch stopWatch = StopWatch.createStarted();
        log.info("上传文件到OSS开始");

        long currentTimeMillis = System.currentTimeMillis();
        String filePath = MODEL_OSS_PATH_PREFIX + currentTimeMillis + "/" + file.getOriginalFilename();
        OssClient storage = OssFactory.instance();

        try {
            UploadResult uploadResult = storage.upload(file.getBytes(), filePath);
            // 计时
            stopWatch.stop();
            log.info("上传文件到OSS完成, 耗时：{}", stopWatch.getTime());
            return uploadResult;
        } catch (IOException e) {
            throw new ServiceException("文件上传失败: " + e.getMessage());
        }
    }

    /**
     * 解析文件名和后缀
     */
    private FileNameInfo parseFileName(String originalFileName) {
        String fileName = originalFileName;
        String suffix = "";

        int dotIndex = originalFileName.lastIndexOf('.');
        if (dotIndex >= 0) {
            suffix = originalFileName.substring(dotIndex);
            fileName = originalFileName.substring(0, dotIndex);
        }

        return new FileNameInfo(fileName, suffix);
    }

    /**
     * 根据上传结果填充模型业务对象
     */
    private void populateModelBoFromUpload(IsfdAcousticsModelBo bo, MultipartFile file,
                                           String originalFileName, UploadResult uploadResult) {
        FileNameInfo fileNameInfo = parseFileName(originalFileName);

        bo.setFileName(fileNameInfo.fileName);
        bo.setOriginalName(originalFileName);
        bo.setUrl(uploadResult.getUrl());
        bo.setFilePath(uploadResult.getFilename());
        bo.setMeshFormat(fileNameInfo.suffix);
        bo.setFileSize(file.getSize());
    }

    /**
     * 更新已存在的模型
     */
    private Boolean updateExistingModel(MultipartFile file, IsfdAcousticsModelBo bo,
                                        IsfdAcousticsModel existingModel) {
        String originalFileName = extractFileName(file.getOriginalFilename());
        UploadResult uploadResult = uploadFileToOss(file);

        populateModelBoFromUpload(bo, file, originalFileName, uploadResult);
        bo.setId(existingModel.getId());

        return edit(bo);
    }

    /**
     * 验证模型更新参数
     */
    private void validateModelUpdate(IsfdAcousticsModelBo bo) {
        if (bo.getId() == null) {
            throw new BaseException("模型ID不能为空");
        }

        IsfdAcousticsModel existingModel = baseMapper.selectById(bo.getId());
        if (existingModel == null) {
            throw new BaseException("模型不存在");
        }

        // 如果修改了文件名，检查是否重复
        if (!existingModel.getOriginalName().equals(bo.getOriginalName())) {
            if (isModelExists(bo.getOriginalName(), bo.getId())) {
                throw new BaseException("模型文件名已存在: " + bo.getOriginalName());
            }
        }
    }

    /**
     * 准备下载响应
     */
    private void prepareDownloadResponse(HttpServletResponse response, String fileName) {
        FileUtils.setAttachmentResponseHeader(response, fileName);
        response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE + "; charset=UTF-8");
    }


    private IsfdAcousticsModelVo signUrl(IsfdAcousticsModelVo modelVo) {
        OssClient storage = OssFactory.instance();
        modelVo.setUrl(storage.OssSignedUrl(modelVo.getFilePath()));
        return modelVo;
    }

    private IsfdAcousticsModelVo matchingUrl(IsfdAcousticsModelVo modelVo) {
        OssClient storage = OssFactory.instance();
        // 仅修改桶类型为 private 的URL，临时URL时长为120s
        if (AccessPolicyType.PRIVATE == storage.getAccessPolicy()) {
            modelVo.setUrl(storage.getPrivateUrl(modelVo.getFileName(), 120));
        }
        return modelVo;
    }

    /**
     * 文件名信息内部类
     */
    private static class FileNameInfo {
        final String fileName;
        final String suffix;

        FileNameInfo(String fileName, String suffix) {
            this.fileName = fileName;
            this.suffix = suffix;
        }
    }
}
